package edu.pitt.cs.cs2410pj2.core;

import java.util.ArrayList;
import java.util.Hashtable;
import java.util.Iterator;

import edu.pitt.cs.cs2410pj2.datamodel.DirectoryEntry;
import edu.pitt.cs.cs2410pj2.datamodel.State;
import edu.pitt.cs.cs2410pj2.util.AddrInfo;
import edu.pitt.cs.cs2410pj2.util.AddrProcessor;
import edu.pitt.cs.cs2410pj2.util.ByteStrOperator;
import edu.pitt.cs.cs2410pj2.util.Parameters;

public class TileDirectory {
	private Parameters para;
	private Hashtable<String, DirectoryEntry> items;
	
	public TileDirectory(Parameters para) {
		this.para = para;
		items = new Hashtable<String,DirectoryEntry>();
	}
	
	public Hashtable<String, DirectoryEntry> getItems() {
		return items;
	}
	
	public boolean isBlockCached(String blockAddr) {
		AddrInfo info = AddrProcessor.processAddr(blockAddr, para.p, para.n2, para.a2, para.b);
		String key = generateKey(info.getSetIndex(),info.getCacheTag());
		if(items.containsKey(key)) {
			return true;
		} else {
			return false;
		}
	}
	
	public void addOwner(String blockAddr, int tileId) {
		AddrInfo info = AddrProcessor.processAddr(blockAddr, para.p, para.n2, para.a2, para.b);
		String key = generateKey(info.getSetIndex(),info.getCacheTag());
		items.get(key).addOwner(tileId);
	}
	
	public void clearOwners(String blockAddr) {
		AddrInfo info = AddrProcessor.processAddr(blockAddr, para.p, para.n2, para.a2, para.b);
		String key = generateKey(info.getSetIndex(),info.getCacheTag());
		items.get(key).clearOwners();
	}
	
	public void removeOwner(String blockAddr, int tileId) {
		AddrInfo info = AddrProcessor.processAddr(blockAddr, para.p, para.n2, para.a2, para.b);
		String key = generateKey(info.getSetIndex(),info.getCacheTag());
		items.get(key).removeOwner(tileId);
	}
	
	public ArrayList<Integer> getOwners(String blockAddr) {
		AddrInfo info = AddrProcessor.processAddr(blockAddr, para.p, para.n2, para.a2, para.b);
		String key = generateKey(info.getSetIndex(),info.getCacheTag());
		return items.get(key).getOwners();
	}
	
	public ArrayList<Integer> getOwnersFromKey(String key) {
		return items.get(key).getOwners();
	}
	
	public void setBlockBusy(String blockAddr,boolean busy) {
		AddrInfo info = AddrProcessor.processAddr(blockAddr, para.p, para.n2, para.a2, para.b);
		String key = generateKey(info.getSetIndex(),info.getCacheTag());
		this.items.get(key).setBusy(busy);
	}
	
	public int getBlockState(String blockAddr) {
		AddrInfo info = AddrProcessor.processAddr(blockAddr, para.p, para.n2, para.a2, para.b);
		String key = generateKey(info.getSetIndex(),info.getCacheTag());
		return items.get(key).getState();
	}
	
	public void changeBlockState(String blockAddr, int state) {
		AddrInfo info = AddrProcessor.processAddr(blockAddr, para.p, para.n2, para.a2, para.b);
		String key = generateKey(info.getSetIndex(),info.getCacheTag());
		items.get(key).setState(state);
	}
	
	public boolean isBlockBusy(String blockAddr) {
		AddrInfo info = AddrProcessor.processAddr(blockAddr, para.p, para.n2, para.a2, para.b);
		String key = generateKey(info.getSetIndex(),info.getCacheTag());
		return items.get(key).isBusy();
	}
	
	public static String generateKey(int setIndex, int cacheTag) {
		return ByteStrOperator.decToBinary(setIndex)+ByteStrOperator.decToBinary(cacheTag);
	}
	
	public void addBlock(String blockAddr, int state) {
		DirectoryEntry entry = new DirectoryEntry();
		entry.setBlockAddr(blockAddr);
		entry.setState(state);
		entry.setBusy(false);
		AddrInfo info = AddrProcessor.processAddr(blockAddr, para.p, para.n2, para.a2, para.b);
		String key = generateKey(info.getSetIndex(),info.getCacheTag());
		items.put(key, entry);
	}
	
	public void removeBlock(String blockAddr) {
		AddrInfo info = AddrProcessor.processAddr(blockAddr, para.p, para.n2, para.a2, para.b);
		String key = generateKey(info.getSetIndex(),info.getCacheTag());
		this.items.remove(key);
	}
	
	
	public void removeBlockKey(int setIndex, int cacheTag) {
		String key = generateKey(setIndex,setIndex);
		this.items.remove(key);
	}
	
	public void removeBlockKey(String key) {
		this.items.remove(key);
	}
	
	public String toString() {
		String msg = "";
		Iterator<String> it = items.keySet().iterator();
		while(it.hasNext()) {
			String key = it.next();
			String content = "BLOCK:"+items.get(key).getBlockAddr()+",state:"+State.getStateName(items.get(key).getState());
			msg+=content+"\n";
		}
		return msg;
	}
	
	public static void main(String[] args) {
		System.out.println(ByteStrOperator.hexToDec("0xa8c832c0"));
		System.out.println(ByteStrOperator.decToBinary(ByteStrOperator.hexToDec("0xa8c832c0")));
		System.out.println(ByteStrOperator.decToBinary(ByteStrOperator.hexToDec("0xb28f32c4")));
		System.out.println(ByteStrOperator.hexToBinary("0xa8c832c0"));
		AddrInfo info0 = AddrProcessor.processAddr("0xa8c832c0",4 ,20, 2, 6);
		AddrInfo info1 = AddrProcessor.processAddr("0xb28f32c4", 4, 20, 2, 6);
		System.out.println(generateKey(info0.getSetIndex(),info0.getCacheTag()));
		System.out.println(generateKey(info1.getSetIndex(),info1.getCacheTag()));
		
		AddrInfo info2 = AddrProcessor.processAddr("0xa998c2ac", 4, 20, 2, 6);
		System.out.println(info2.getTileId());
		System.out.println(info2.getSetIndex());
		System.out.println(info2.getCacheTag());
	}
}
